/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.etrs.formathlon.persistance.formation;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.PersistenceException;
import net.etrs.formathlon.entity.formation.Formation;
import net.etrs.formathlon.entity.formation.Inscription;
import net.etrs.formathlon.entity.formation.Participation;
import net.etrs.formathlon.entity.formation.Seance;
import net.etrs.formathlon.entity.formation.Session;
import net.etrs.formathlon.entity.utilisateur.Personnel;
import net.etrs.formathlon.entity.utilisateur.Utilisateur;
import net.etrs.formathlon.metier.exception.MetierException;
import net.etrs.formathlon.metier.service.EntiteManager.NiveauHierarchique;
import net.etrs.formathlon.metier.utils.DateUtils;
import net.etrs.formathlon.metier.utils.FileUtils;
import net.etrs.formathlon.metier.utils.email.MailManager;
import net.etrs.formathlon.metier.utils.email.MailParticipationDTO;
import net.etrs.formathlon.metier.utils.pdf.PDFManager;
import net.etrs.formathlon.persistance.utilisateur.UtilisateurFacade;
import net.etrs.formathlon.persistance.utils.AbstractFacade;
import net.etrs.formathlon.persistance.utils.JpaRequestNames;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.mail.EmailException;

/**
 * Facade pour la gestion de l'entité Session.
 * @author OPIF
 */
@Stateless
public class SessionFacade extends AbstractFacade<Session> {

    @EJB
    private UtilisateurFacade utilisateurFacade;
    @EJB
    private InscriptionFacade inscriptionFacade;
    @EJB
    private ParticipationFacade participationFacade;
    @EJB
    private SeanceFacade seanceFacade;

    /**
     * Permet de savoir si une session est ouverte.
     * @param session
     * @return 
     */
    public boolean isOuverte(Session session) {
        if (session.getEtat().equals(Session.EtatSession.OUVERT)) {
            return true;
        }
        return false;
    }

    /**
     * Permet de savoir si une session est fermée.
     * @param session
     * @return 
     */
    public boolean isFermee(Session session) {
        if (session.getEtat().equals(Session.EtatSession.FERME)) {
            return true;
        }
        return false;
    }

    /**
     * Permet de savoir si une session est verrouillée.
     * @param session
     * @return 
     */
    public boolean isVerrouillee(Session session) {
        return session.getEtat().equals(Session.EtatSession.VERROUILLE);
    }

    /**
     * Permet de savoir si une session est terminée.
     * @param session
     * @return 
     */
    public boolean isTerminee(Session session) {
        if (session.getEtat().equals(Session.EtatSession.TERMINE)) {
            return true;
        }
        return false;
    }

    /**
     * Permet de savoir si une session peut être modifiée.
     * @param session
     * @return 
     */
    public boolean isModifiable(Session session) {
        if (session.getEtat().equals(Session.EtatSession.CREATION)) {
            return true;
        }
        return false;
    }

    /**
     * Permet de savoir si une session peut être supprimée.
     * @param session
     * @return 
     */
    public boolean isSupprimable(Session session) {
        //si creation ou ouvert et pas d'inscrit
        if (session.getEtat().equals(Session.EtatSession.CREATION)
                || (session.getEtat().equals(Session.EtatSession.OUVERT)
                && inscriptionFacade.getNbInscrit(session) == 0)) {
            return true;
        }
        return false;
    }

    /**
     * Ouvre la saisie à l'inscription.
     * @param session 
     */
    public void ouvrir(Session session) {
        session.setEtat(Session.EtatSession.OUVERT);
        super.update(session);
    }

    /**
     * Méthode qui clôture une session.
     * Envoie les mails en fonction des différents cas suite à la clôture.
     * @param session 
     */
    public void cloturer(Session session) {
        try {
            //on n'a pas atteint le minimum requis
            if (session.getListInscription().size() < session.getNbPlaceMini()) {
                //annulation de la session par manque d'inscrit
                session.setEtat(Session.EtatSession.TERMINE);
                archiver(session);
                for (Inscription inscription : session.getListInscription()) {
                    inscription.setEtat(Inscription.Etat.ANNULEE);
                    inscriptionFacade.update(inscription);
                }
                MailManager.envoiAnnulationSession(session);
            } else {
                session.setEtat(Session.EtatSession.FERME);
                //les demandes en cours sont annulées
                for (Inscription inscription : inscriptionFacade.getListInscriptionNonValidees(session)) {
                    inscription.setEtat(Inscription.Etat.ANNULEE);
                    inscriptionFacade.update(inscription);
                }
                MailManager.envoiInformationSession(session);
            }
        } catch (EmailException ex) {
            Logger.getLogger(SessionFacade.class.getName()).log(Level.SEVERE, null, ex);
        }
        super.update(session);
    }

    /**
     * Retourne les sessions d'une formation qui ne sont pas terminées.
     * @return liste de sessions
     */
    public List<Session> getListSessionEnCours(Formation formation) {
        List<Session> retour = new ArrayList<Session>();
        for (Session session : formation.getListSession()) {
            //il faut que la session ne soit pas terminee (
            if (!session.getEtat().equals(Session.EtatSession.TERMINE)
                    && !session.getEtat().equals(Session.EtatSession.VERROUILLE)
                    && !session.isArchive()) {
                retour.add(session);
            }
        }
        return retour;
    }

    /**
     * Affinage du l'instanciation d'une nouvelle session.
     * @return La nouvelle session
     */
    @Override
    public Session newInstance() {
        Session session = super.newInstance();
        session.setEtat(Session.EtatSession.CREATION);
        return session;
    }

    /**
     * Les dates de la sessions dépendent des dates de ses séances.
     * @param session 
     */
    public void operationSurDate(Session session) {
        Collections.sort(session.getListSeance(), new BeanComparator("dateHeureDebut"));
        session.setDateDebut(session.getListSeance().get(0).getDateHeureDebut());
        session.setDateFin(session.getListSeance().get(session.getListSeance().size() - 1).getDateHeureDebut());
    }

    /**
     * Affinage lors de la création d'une session.
     * @param session 
     */
    @Override
    public void create(Session session) {
        operationSurDate(session);
        super.create(session);
    }

    @Override
    public void archiver(Session t) throws PersistenceException {
        super.archiver(t);
        for (Seance seance : t.getListSeance()) {
            seanceFacade.archiver(seance);
        }
    }

    /**
     * Renvoie les sessions ouvertes à l'inscription.
     * @return 
     */
    public List<Session> getSessionOuvertes() {
        return getEm().createNamedQuery(JpaRequestNames.LISTE_SESSION_SUIVANT_ETAT).setParameter("unEtat", Session.EtatSession.OUVERT).getResultList();
    }

    /**
     * Renvoie les sessions suivant l'etat donné en paramètre.
     * @param etat
     * @return 
     */
    public List<Session> getSessionSuivantEtat(Session.EtatSession etat) {
        return getEm().createNamedQuery(JpaRequestNames.LISTE_SESSION_SUIVANT_ETAT).setParameter("unEtat", etat).getResultList();
    }

    /**
     * Retourne l'ensemble des sessions qu'un instruteur dispense.
     * @param instructeur L'utilisateur qui dispense la session.
     * @return La liste des sessions dispensées.
     */
    public List<Session> getListSessionDispensees(Personnel instructeur) {
        return getEm().createNamedQuery(JpaRequestNames.LISTE_SESSION_DISPENSEES).setParameter("instructeur", instructeur).getResultList();
    }

    /**
     * Verrouillage d'une session.
     * Une session ne peut être verrouillée que si elle est fermée.
     * Toutes les inscriptions sont également verrouillées.
     * @param session La session à verrouiller.
     */
    public void verrouiller(Session session) throws MetierException, EmailException {
        if (isFermee(session)) {
            session.setEtat(Session.EtatSession.VERROUILLE);
            update(session);
            List<Inscription> listValid = inscriptionFacade.getListInscriptionValidees(session);
            for (Inscription inscription : listValid) {
                inscription.setEtat(Inscription.Etat.VERROUILLEE);
                inscriptionFacade.update(inscription);
            }
            //les inscriptions en liste d'attente sont annulées
            if (listValid.size() > session.getNbPlaceMax()) {
                List<Utilisateur> listeComplementaire = new ArrayList<Utilisateur>(listValid.size() - session.getNbPlaceMax());
                for (int i = session.getNbPlaceMax(); i < listValid.size(); i++) {
                    Inscription inscription = listValid.get(i);
                    inscription.setEtat(Inscription.Etat.ANNULEE);
                    listeComplementaire.add(inscription.getPersonnel().getUtilisateur());
                    inscriptionFacade.update(inscription);
                }
                MailManager.envoiAnnulationListeComplementaire(session, listeComplementaire);
            }
        } else {
            throw new MetierException(MetierException.Type.INSCRIPTION_ETAT_NON_VALIDE);
        }
    }

    /**
     * Création d'une feuille d'émargement pour une session.
     * @param session La session à émarger.
     * @return Le flux du fichier généré.
     */
    public InputStream getFeuilleEmargement(Session session) throws MetierException {
        try {
            // Préparation des données
            String formateur = session.getInstructeur() != null ? session.getInstructeur().toString() : "";
            String detail = String.format(
                    "%s - %s du %s au %s (Insctructeur : %s)",
                    session.getFormation().getDomaine().getLibelle(),
                    session.getFormation().getLibelle(),
                    DateUtils.getDateFormatee(session.getDateDebut()),
                    DateUtils.getDateFormatee(session.getDateFin()),
                    formateur);
            List<Personnel> listPersonnel = new LinkedList<Personnel>();
            for (Inscription inscription : inscriptionFacade.getListInscriptionValidees(session)) {
                listPersonnel.add(inscription.getPersonnel());
            }
            Properties prop = FileUtils.getConfigProperties(this.getClass());

            //création du fichier
            String file = PDFManager.generateEmargement(detail, session.getListSeance(), listPersonnel, prop);
            return new FileInputStream(file);
        } catch (Exception ex) {
            throw new MetierException(MetierException.Type.PDF_EMARGEMENT_ERREUR_TECHNIQUE, ex);
        }
    }

    /**
     * Valider une fin de session avec une liste de participation.
     * @param session
     * @param listInscriptionAyantParticipe 
     */
    public void validerParticipationsEtArchiver(Session session,
            List<Inscription> listInscriptionAyantParticipe) throws MetierException {
        try {
            //Préparation du mail de compte rendu
            MailParticipationDTO mailParticipation = new MailParticipationDTO(session);

            //Pour l'ensemble des inscription de la session
            for (Inscription inscription : session.getListInscription()) {

                //création de la liste des supérieurs à la personne inscrite
                List<Utilisateur> listChef = new LinkedList<Utilisateur>();

                //recherche des chefs de cours (ou adjoints) du participant
                listChef.addAll(utilisateurFacade.getSuperieurs(
                        inscription.getPersonnel().getUtilisateur().getEntite(),
                        NiveauHierarchique.COURS));

                //ajout des chefs de div (ou adjoints) du participant
                listChef.addAll(utilisateurFacade.getSuperieurs(
                        inscription.getPersonnel().getUtilisateur().getEntite(),
                        NiveauHierarchique.DIVISION));

                boolean participe = false;

                //Si l'inscription fait partie des inscriptions ayant été participées
                if (listInscriptionAyantParticipe.contains(inscription)) {

                    //marquage de l'inscription en tant que participation
                    inscription.setEtat(Inscription.Etat.PARTICIPEE);

                    Participation participation = participationFacade.getParticipation(inscription.getPersonnel(), inscription.getSession().getFormation());
                    //création de la participation
                    //ou écrasement 
                    if (participation == null) {
                        participation = participationFacade.newInstance(inscription);
                        participationFacade.create(participation);
                    } else {
                        participation.setDateFormation(inscription.getSession().getDateDebut());
                        participationFacade.update(participation);
                    }

                    participe = true;
                } else {
                    //marquage de l'inscription en tant que non participation
                    inscription.setEtat(Inscription.Etat.NON_PARTICIPEE);
                }

                //archiver l'inscription
                inscriptionFacade.archiver(inscription);

                //ajouter les destinataires du mail de compte rendu
                for (Utilisateur chef : listChef) {
                    mailParticipation.getDestinataire(chef).setParticipationSubordonne(inscription.getPersonnel(), participe);
                }
            }
            //archiver la session
            session.setEtat(Session.EtatSession.TERMINE);
            archiver(session);

            //envoi des mails
            MailManager.envoiRapportFinSession(mailParticipation);
        } catch (Exception ex) {
            throw new MetierException(MetierException.Type.ARCHIVAGE_SESSION_VALIDATION_PARTICIPATION_ERREUR_TECHNIQUE, ex);
        }
    }

    /**
     * Envoi de mail en cas de changement de liste.
     * Secondaire à principale.
     * @param inscription 
     */
    public void desistement(Inscription inscription) {
        Inscription inscri, tmp;
        if (inscription.getSession().getListInscription().size() > inscription.getSession().getNbPlaceMax()) {
            for (int i = 0; i < inscription.getSession().getNbPlaceMax(); i++) {
                inscri = inscription.getSession().getListInscription().get(i);
                if (inscription.equals(inscri)) {
                    try {
                        tmp = inscription.getSession().getListInscription().get(inscription.getSession().getNbPlaceMax());
                        tmp.setEtat(Inscription.Etat.VALIDEE_DIVISION);
                        inscriptionFacade.update(tmp);
                        MailManager.envoieModificationInscription(tmp);
                    } catch (EmailException ex) {
                        Logger.getLogger(SessionFacade.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }
}
